package com.longxiao.service;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import java.util.*;

import javax.annotation.Resource;

import com.longxiao.para.AddUserParam;
import com.longxiao.para.UserSearchPara;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.longxiao.constants.LXExceptions;
import com.longxiao.entity.StudentLecture;
import com.longxiao.entity.TeacherLecture;
import com.longxiao.entity.TempUser;
import com.longxiao.entity.User;
import com.longxiao.enums.ResultCode;
import com.longxiao.enums.SysConfigKey;
import com.longxiao.enums.UserType;
import com.longxiao.exception.LXException;
import com.longxiao.mapper.ClassroomStuMapper;
import com.longxiao.mapper.StudentLectureMapper;
import com.longxiao.mapper.TeacherLectureMapper;
import com.longxiao.mapper.TempUserMapper;
import com.longxiao.mapper.UserMapper;
import com.longxiao.service.util.DateUtil;
import com.longxiao.service.util.DecimalUtil;
import com.longxiao.service.util.MD5Util;
import com.longxiao.service.util.RedisClient;
import com.longxiao.validator.LXValidator;
import com.longxiao.validator.ValidResult;
import com.longxiao.vo.LoginVo;
import com.longxiao.vo.RegisterMobileVo;
import com.longxiao.vo.RegisterVo;
import com.longxiao.vo.StuUserVo;
import com.longxiao.vo.TchUserVo;
import com.longxiao.vo.TempUserVo;
import com.longxiao.vo.UpdateTchVo;
import com.longxiao.vo.UpdateUserVo;
import com.longxiao.vo.UserSearchParaVo;
import com.longxiao.vo.UserVo;

/**
 * Created by yusong on 2017/4/27.
 * 用户服务接口
 */
@Service
public class UserService {

    private final Logger logger = LoggerFactory.getLogger(UserService.class);
    private static final Random random = new Random(1000);

    @Resource
    private UserMapper userMapper;

    @Resource
    private TeacherLectureMapper teacherLectureMapper;

    @Resource
    private StudentLectureMapper studentLectureMapper;

    @Resource
    private ClassroomStuMapper classroomStuMapper;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private SmsService smsService;
    
    private String PRE_STU = "1";
    
    private String PRE_TCH = "2";
    @Resource
    private SysConfigService sysConfigService;
    @Resource
    private TempUserMapper tempUserMapper;

    /*
    *分页搜索用户信息
    */
    public List<UserVo> listUserByPage(UserSearchPara userSearchPara){
        List<User> entities = userMapper.getListByPage(userSearchPara);
        List<UserVo> res = new ArrayList<UserVo>();
        if (null != entities){
            for(User entity : entities){
                UserVo userVo = new UserVo(entity);
                res.add(userVo);
            }
        }
        return res;
    }

    /*
    *获取用户总数
    */
    public Integer count(UserSearchPara userSearchPara){
        return userMapper.count(userSearchPara);
    }

    /*
    *新增临时用户
    */
    public void addTempUser(AddUserParam addUserParam) throws Exception {
        logger.info("addTempUser|新增临时用户|req:" + addUserParam);
        ValidResult validResult = LXValidator.validation(addUserParam);
        if (!validResult.isValid()) {
            logger.info("addTempUser|新增临时用户|validResult{}:", validResult);
            throw new LXException(ResultCode.PARAM_ERROR, "CN");
        }
        String mobile = addUserParam.getMobile();
        // 手机号是否存在
        if (isExistsMobile(mobile)) {
            // 手机号已经存在，返回错误码
            throw new LXException(ResultCode.MOBILE_IS_EXISTS, "CN");
        }
        TempUser tempUser = new TempUser();
        tempUser.setUserId(generateToken(String.valueOf(addUserParam.getUserType())));
        tempUser.setUserType(addUserParam.getUserType());
        tempUser.setPassword(MD5Util.md5(addUserParam.getPassword()));

        tempUser.setMobile(mobile);
        tempUser.setCreatedTime(new Date());
        tempUser.setModifiedTime(tempUser.getCreatedTime());
        tempUserMapper.insert(tempUser);

        String token = generateToken("token");
        TempUserVo tmpUserVo = new TempUserVo(tempUser.getUserType(), tempUser.getMobile(), token, tempUser.getUserId());
        logger.info("addTempUser|新增临时用户|完成|return:{}", tmpUserVo);
        addUserParam.setUserId(tempUser.getUserId());
        //新增永久用户
        addUser(addUserParam,token);
    }


    /*
    *新增用户
    */
    @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
    public UserVo addUser(RegisterVo registerVo, String token) throws LXException{
        logger.info("addUser|新增用户|req:" + registerVo);
        ValidResult validResult = LXValidator.validation(registerVo);
        if (!validResult.isValid()) {
            logger.error("addUser|参数错误|validResult:{}",validResult);
            throw new LXException(ResultCode.PARAM_ERROR, "CN");
        }
        User user = userMapper.selectByUserId(registerVo.getUserId());
        if (user != null) {
            logger.info("addUser|userId已存在|userId:{}| param:{}", registerVo.getUserId(), registerVo);
            throw new LXException(ResultCode.PARAM_NOT_LEGAL, "CN");
        }
        // 邮箱是否存在
        if (isExistsEmail(registerVo.getEmail())) {
            // 邮箱已经存在，返回错误码
            throw new LXException(ResultCode.EMAIL_IS_EXISTS, "CN");
        }
        //获取临时用户信息
        TempUser tempUser = tempUserMapper.selectByUserId(registerVo.getUserId());
        if(null == tempUser){
            throw new LXException(ResultCode.MOBILE_NOT_FOUND, "CN");
        }
        user = initUser(registerVo,tempUser);
        int insertNum = 0;
        insertNum = userMapper.insert(user);
        UserVo userVo = null;
        logger.info("addUser|新增用户|num:" + insertNum);
        if (UserType.Stu.getCode() == user.getUserType()) {
            StudentLecture studentLecture = initStuLecture(user.getUserId());
            userVo = new StuUserVo(user,studentLecture);
            logger.info("addUser|插入学生信息|"+userVo);
        } else {
            TeacherLecture teacherLecture = initTchLecture(user.getUserId(),registerVo.getRate());
            userVo = new TchUserVo(user,teacherLecture);
            logger.info("addUser|插入教师信息|"+userVo);
        }

        UserVo redisUserVo = new UserVo(user);
        redisUserVo.setToken(token);
        Gson gson = new Gson();
        gson.toJson(redisUserVo);
        redisClient.setEx(token, gson.toJson(redisUserVo), 31 * 24 * 60 * 60);
        redisClient.setEx((redisUserVo.getUserId() + ":tkn"), token, 31 * 24 * 60 * 60);

        return userVo;
    }

    /*
    *获取用户信息
    */
    public UserVo getUserInfo(String userId) throws LXException{
        User user = userMapper.selectByUserId(userId);
        logger.info("getUserInfo|查询到用户基本信息|userId:"+userId+"|"+user);
        if (null == user) {
            logger.error("getUserInfo|未找到指定用户信息|userId:"+userId);
            throw LXExceptions.NO_RECORD_ERROR;
        }
        if (UserType.Tch.getCode() == user.getUserType()) {
            TeacherLecture teacherLecture = teacherLectureMapper.selectByUserId(userId);
            TchUserVo tchUserVo = new TchUserVo(user,teacherLecture);
//          查询老师所有的学生个数
            int stuCount = classroomStuMapper.countStu(userId);
            tchUserVo.setStuCount(stuCount);
            logger.info("getUserInfo|查询到教师信息|"+tchUserVo);
            return tchUserVo;
        }else{
            StudentLecture studentLecture = studentLectureMapper.selectByUserId(userId);
            StuUserVo stuUserVo = new StuUserVo(user,studentLecture);
            stuUserVo.setMaxLevel(sysConfigService.getValByKeyword(SysConfigKey.MAX_LEVEL, "C12"));
            logger.info("getUserInfo|查询到学生信息|"+stuUserVo);
            return stuUserVo;
        }
    }

    /*
    *更新老师信息
    */
    public TchUserVo updateTch(UpdateTchVo updateTchVo) throws LXException{
        User user = updateUser(updateTchVo);
        String userId = updateTchVo.getUserId();
        logger.info("updateTch|更新后基本信息|"+user);
//        if(updateTchVo.getRate() != null){
//            int counet = teacherLectureMapper.updateRate(new UpdateTchRate(userId,updateTchVo.getRate()));
//            logger.info("updateTch|更新费率|userId:"+userId+"count:"+counet);
//        }
        TeacherLecture teacherLecture = teacherLectureMapper.selectByUserId(userId);
        TchUserVo tchUserVo = new TchUserVo(user,teacherLecture);
        logger.info("updateTch|更新后老师信息|"+tchUserVo);
        return tchUserVo;
    }

    public String uploadImg(String fileName,InputStream is) throws LXException {
    	
    	try {
			String prePath = sysConfigService.getValByKeyword(SysConfigKey.USER_FILE_LOCAL);
			String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
			String realFileName = UUID.randomUUID() + "." + ext;
			String httpPath = this.getPath(sysConfigService.getValByKeyword(SysConfigKey.USER_FILE_HTTP),realFileName);
			this.writeFile(prePath, realFileName, is);
			logger.info("用户上传文件：{}，{}",fileName,httpPath);
			return httpPath;
		} catch (IOException e) {
			logger.error(fileName + "上传图片失败：" + e.getMessage(),e);
		}
    	throw new LXException(ResultCode.FILE_UPLOAD_FAIL.getCode(), ResultCode.FILE_UPLOAD_FAIL.getCn());
    }
    
    /*
	 * 向指定路径写入文件写入文件
	 */
	private void writeFile(String fileDirectory,String fileName,InputStream is) throws IOException, LXException {
		File directory = new File(fileDirectory);
		
		if(directory.exists() && directory.isDirectory()) {
			File file = new File(fileDirectory + "/" + fileName);
			OutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
			byte[] content = new byte[is.available()];
			is.read(content);
			fos.write(content, 0, content.length);
			fos.flush();
			fos.close();
			return ;
		}
		
		if(!directory.exists()) {
			if(directory.mkdirs()) {
				File file = new File(fileDirectory + "/" + fileName);
				OutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
				byte[] content = new byte[is.available()];
				is.read(content);
				fos.write(content, 0, content.length);
				fos.flush();
				fos.close();
				return ;
			}
		}
		
		throw new LXException(ResultCode.FILE_UPLOAD_FAIL.getCode(),ResultCode.FILE_UPLOAD_FAIL.getCn());
	}
    
    private String getPath(String firstPath, String secondPath) {
		if(firstPath.endsWith("/")) {
			if(secondPath.startsWith("/")) {
				return firstPath + secondPath.substring(1);
			}
		} else {
			if(!secondPath.startsWith("/")) {
				return firstPath + "/" + secondPath;
			}
		}
		return firstPath + secondPath;
	}
    
    /*
    *更新学生信息
    */
    public StuUserVo updateStu(UpdateUserVo updateUserVo) throws LXException{
        User user = updateUser(updateUserVo);
        String userId = updateUserVo.getUserId();
        logger.info("updateStu|更新后基本信息|"+user);
        StudentLecture studentLecture = studentLectureMapper.selectByUserId(userId);
        StuUserVo updateStu = new StuUserVo(user,studentLecture);
        return updateStu;
    }

    /*
    *更新用户基本信息
    */
    public User updateUser(UpdateUserVo updateUserVo){
        int count = userMapper.updateUser(updateUserVo);
        logger.info("updateUser|更新用户基本信息|count:"+count+"|"+updateUserVo);
        return userMapper.selectByUserId(updateUserVo.getUserId());
    }


    /*
    *获取教师信息
    */
    public TchUserVo getTchInfo(String userId) throws LXException{
        return (TchUserVo)getUserInfo(userId);
    }

    /*
    *获取学生信息
    */
    public StuUserVo getStuInfo(String userId) throws LXException{
        return (StuUserVo)getUserInfo(userId);
    }

    /*
    *登录
    * 登录成功返回UserVo
    */
    public UserVo login(LoginVo loginVo) throws Exception {
        logger.info("login|登录请求|===={}", loginVo);
        UserSearchParaVo userSearchParaVo = new UserSearchParaVo(loginVo.getMobile(),loginVo.getEmail());
        User user = userMapper.select(userSearchParaVo);
        UserVo res = null;
        int isSupplementaryData = 0;// 是否需要补充用户资料 0不需要补充  1需要
        if (null == user) {
            // 用户不存在，是否为临时用户
            TempUser tmpUser = tempUserMapper.selectByMobile(loginVo.getMobile());
            if (tmpUser == null) {
                // 也不是临时用户，提示用户名或密码错误
                logger.info("login|用户表，临时用户表都没有找到用户信息|返回失败|===={}", loginVo);
                throw new LXException(ResultCode.LOGIN_FAIL, "EN");
            }
            // 临时用户转化成用户对象
            user = tmpUser.toUserObj();
            res = tmpUser.toUserVoObj();
            isSupplementaryData = 1;
        }
        String md5Pass = MD5Util.md5(loginVo.getPassword());
        if (!user.getPassword().equals(md5Pass)) {
            logger.info("login|用户密码错误|===={}" + loginVo);
            throw LXExceptions.WRONG_PASS_ERROR;
        }
        if (isSupplementaryData == 1) {
            // 临时用户登录流程
        } else {
            // 正常登录流程
            res = getUserInfo(user.getUserId());
        }
        res.setIsSupplementaryData(isSupplementaryData);
        String token = generateToken("token");
        res.setToken(token);
        Gson gson = new Gson();
        // 对原来的token失效
        String oldToken = redisClient.get(res.getUserId() + ":tkn");
        if (!StringUtils.isEmpty(oldToken)) {
            redisClient.expire(oldToken, 1);
        }
        // 设置新的token
        redisClient.setEx(token, gson.toJson(res), 31 * 24 * 60 * 60);
        redisClient.setEx((res.getUserId() + ":tkn"), token, 31 * 24 * 60 * 60);
        logger.info("login|登录请求|成功|===={}", res);
        return res;
    }

    /**
     * token登录操作
     * @param token
     * @return
     */
    public UserVo tokenLogin(String token, UserVo user) {
        boolean bool = redisClient.exists(token);
        if (bool == Boolean.FALSE) {
            // token不存在
        }
        String newToken = generateToken(token);
        user.setToken(newToken);
        Gson gson = new Gson();
        // 原来token失效
        redisClient.expire(token, 1);
        // 设置新的token
        redisClient.setEx(newToken,gson.toJson(user), 31 * 24 * 60 * 60);
        redisClient.setEx((user.getUserId() + ":tkn"), token, 31 * 24 * 60 * 60);
        logger.info("tokenLogin|token登录|user:", user);
        return user;
    }

    /*
    *检查登录状态
    * 还在登录状态返回UserVo；登录失效返回null
    */
    public UserVo checkLogin(String token){
        if(null == token){
            logger.error("checkLogin|无token");
            return null;
        }
        String jsonStr = redisClient.get(token);
        if(StringUtils.isEmpty(jsonStr)){
            logger.error("checkLogin|未查询到缓存信息|token:"+token);
            return null;
        }
        logger.info("checkLogin|缓存信息|jsonStr:"+jsonStr);
        Gson gson = new Gson();
        UserVo userVo = null;
        try {
            userVo = gson.fromJson(jsonStr,UserVo.class);
        } catch (JsonSyntaxException e) {
            logger.error("checkLogin|缓存数据格式错误|",gson,e);
            return null;
        }
        logger.info("checkLogin|获取到登录信息|"+userVo);
        return userVo;
    }

    /**
     * 退出登录接口
     * @param token
     * @return
     */
    public boolean logout(String token) {
        logger.info("logout|退出登录|token:{}", token);
        redisClient.expire(token, 1);// 设置缓存1秒后过期
        return Boolean.TRUE;
    }
    
    public boolean isTch(String userId) {
    	if(userId == null) {
    		return false;
    	}
    	return userId.startsWith(this.PRE_TCH);
    }

    public boolean isStu(String userId) {
    	if(userId == null) {
    		return false;
    	}
    	return userId.startsWith(this.PRE_STU);
    }

    public boolean isExistsEmail(String email) {
        int n = userMapper.isExistsEmail(email);
        return (n > 0 ? Boolean.TRUE : Boolean.FALSE);
    }

    public boolean isExistsMobile(String mobile) {
        int n = tempUserMapper.isExistsMobile(mobile);
        return (n > 0 ? Boolean.TRUE : Boolean.FALSE);
    }


    /*
    *初始用户
    */
    private User initUser(RegisterVo registerVo ,TempUser tempUser ) throws InvalidParameterException {
        User user = new User();
        Integer userType = tempUser.getUserType();
        if(null == userType || !UserType.valid(userType.intValue())){
            throw new InvalidParameterException("userType");
        }
        user.setUserType(userType);
        user.setUserId(tempUser.getUserId());
        user.setImgUrl(registerVo.getImgUrl());
        user.setNickname(registerVo.getNickname());
        user.setPassword(tempUser.getPassword());
        user.setMobile(tempUser.getMobile());
        if(StringUtils.isEmpty(registerVo.getEmail())){
            throw new InvalidParameterException("email");
        }
        user.setEmail(registerVo.getEmail());
        user.setGender(registerVo.getGender());
        user.setBirthday(DateUtil.parse(registerVo.getBirthday(), "yyyy-MMM-dd"));
        user.setTimezone(registerVo.getTimezone());
        user.setCpScore(0);
        user.setCountry(registerVo.getCountry());
        user.setProvince(registerVo.getProvince());
        user.setCity(registerVo.getCity());
        user.setOccupation(registerVo.getOccupation());
        if(UserType.Stu.getCode() == tempUser.getUserType().intValue() && StringUtils.isEmpty(user.getOccupation())){
            user.setOccupation(UserType.Stu.getDes());
        }
        user.setStatus(Boolean.TRUE);
        user.setCreatedTime(new Date());
        user.setModifiedTime(user.getCreatedTime());
        return user;
    }

    /*
    *初始教师课时信息
    */
    private TeacherLecture initTchLecture(String userId,Integer rate) throws LXException{
        TeacherLecture teacherLecture = new TeacherLecture();
        teacherLecture.setTchId(userId);
        teacherLecture.setRate(rate);
        teacherLecture.setTeachedLectures(0);
        teacherLecture.setPaiedLectures(0);
        teacherLecture.setCreatedTime(new Date());
        teacherLecture.setModifiedTime(teacherLecture.getCreatedTime());
        try {
            teacherLectureMapper.insert(teacherLecture);
        } catch (Exception e) {
            logger.error("initTchLecture|插入老师课时信息失败",e);
            throw LXExceptions.SYSTEM_ERROR;
        }
        logger.info("initStuLecture|插入老师课时信息|"+teacherLecture);
        return teacherLecture;
    }

    /*
    *初始教师课时信息
    */
    private StudentLecture initStuLecture(String userId){
        StudentLecture studentLecture = new StudentLecture();
        studentLecture.setStuId(userId);
        studentLecture.setLevel("0");
        studentLecture.setProgress("0");
        studentLecture.setConsumedLectures(0);
        studentLecture.setBalanceLectures(0);
        studentLecture.setFrozenLectures(0);
        studentLecture.setCommentsCount(0);
        studentLecture.setAveListenScore(DecimalUtil.ZERO);
        studentLecture.setAveOralScore(DecimalUtil.ZERO);
        studentLecture.setAveReadScore(DecimalUtil.ZERO);
        studentLecture.setAveReadScoreLast(DecimalUtil.ZERO);
        studentLecture.setAveListenScoreLast(DecimalUtil.ZERO);
        studentLecture.setAveOralScoreLast(DecimalUtil.ZERO);
        studentLecture.setCreatedTime(new Date());
        studentLecture.setModifiedTime(studentLecture.getCreatedTime());
        studentLectureMapper.insert(studentLecture);
        logger.info("initStuLecture|插入学生课时信息|"+studentLecture);
        return studentLecture;
    }


    /*
    *生成用户唯一ID或者生成登录token
    */
    private static String generateToken(String tokenPrefix) {
        Date now = new Date();
        String s = "LONGXIAO2017" + String.valueOf(now.getTime()) + String.valueOf(random.nextInt());
        String userId = null;
        try {
            userId = tokenPrefix + MD5Util.md5(s);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return userId;
    }



}
